<?php
// +----------------------------------------------------------------------
// | Template 项目模板服务
// +----------------------------------------------------------------------
// | 主要服务于Upload数据处理
// +----------------------------------------------------------------------
// | 错误编码头 226xxx
// +----------------------------------------------------------------------
namespace common\service;

use common\exception\LogicException;
use common\model\DirectoryTemplateModel;
use common\model\ModuleModel;
use common\model\ProjectTemplateModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use Illuminate\Support\Arr;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\module\Module;
use think\Tree;

class TemplateService
{
    /** @var ProjectTemplateModel */
    private $projectTemplateModel;

    /** @var StepCategoryModel */
    private $stepCategoryModel;

    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->projectTemplateModel = model(ProjectTemplateModel::class);
        $this->stepCategoryModel = model(StepCategoryModel::class);
    }

    /**
     * 查询模块的上游模块 (自己可以关联本模块的上游 所以sources里会包含自己模块)
     * @param int $projectId
     * @param int $moduleId
     * @return int[]
     */
    public function getModuleSources(int $projectId, int $moduleId)
    {
        $entityStepCategory = model(StepCategoryModel::class)
            ->where(['entity_id' => $moduleId, 'parent_id' => 0])
            ->cache(C('database.default_query_cache_time'))
            ->find();
        $sources = [$entityStepCategory['id']];
        $projectTemplate = model(ProjectTemplateModel::class)
            ->join("project ON project.project_template_id = project_template.id")
            ->where([
                'project.id' => $projectId,
            ])
            ->field("project_template.step_category_dependency")
            ->find();
        if (empty($projectTemplate)) {
            return $sources;
        }
        $stepCategoryDependency = json_decode($projectTemplate['step_category_dependency'], true);
        /**
         * 没有 一级分类关系配置
         */
        if (empty($stepCategoryDependency['edges'])) {
            return $sources;
        }
        foreach ($stepCategoryDependency['edges'] as $edge) {
            if ($edge['target'] != $entityStepCategory['id']) {
                continue;
            }
            $sources[] = $edge['source'];
        }
        return $sources;
    }

    /**
     * 查询模块的下游模块 (自己可以关联本模块的下游 所以targets里会包含自己模块)
     * @param int $projectId
     * @param int $moduleId
     * @return int[]
     */
    public function getModuleTargets(int $projectId, int $moduleId)
    {
        $targets = [];
        $entityStepCategory = model(StepCategoryModel::class)
            ->where(['entity_id' => $moduleId, 'parent_id' => 0])
            ->cache(C('database.default_query_cache_time'))
            ->find();
        $targets[] = $entityStepCategory['id'];
        $projectTemplate = model(ProjectTemplateModel::class)
            ->join("project ON project.project_template_id = project_template.id")
            ->where([
                'project.id' => $projectId,
            ])
            ->field("project_template.step_category_dependency")
            ->find();
        if (empty($projectTemplate)) {
            return $targets;
        }
        $stepCategoryDependency = json_decode($projectTemplate['step_category_dependency'], true);
        /**
         * 没有 一级分类关系配置 直接返回true
         */
        if (empty($stepCategoryDependency['edges'])) {
            return $targets;
        }
        foreach ($stepCategoryDependency['edges'] as $edge) {
            if ($edge['source'] == $entityStepCategory['id']) {
                $targets[] = $edge['target'];
            }
        }
        return $targets;
    }

    /**
     * 获取模板配置
     * @param $param
     * @return array
     */
    public function getTemplateConfig($param)
    {
        $projectTemplateModel = model(ProjectTemplateModel::class);
        $filter = array_key_exists('filter', $param) ? $param['filter'] : ["id" => $param["template_id"]];
        $configJson = $projectTemplateModel->where($filter)->getField("config");

        $fromConfig = [];
        if (!empty($configJson)) {
            $configData = json_decode($configJson, true);
            if (array_key_exists($param["module_code"], $configData)) {
                if (isset($param["category"])) {
                    // 取回指定的category项目模版配置信息
                    if (array_key_exists($param["category"], $configData[$param["module_code"]])) {
                        $fromConfig = $configData[$param["module_code"]][$param["category"]];
                    }
                } else {
                    // 如果不传category则取回当前模块所有项目模版配置信息
                    $fromConfig = $configData[$param["module_code"]];
                }
            }
        }
        return $fromConfig;
    }

    /**
     * 获取当前项目的模板ID
     * @param $projectId
     * @return mixed
     */
    public function getProjectTemplateID($projectId)
    {
        $projectTemplateModel = model(ProjectTemplateModel::class);
        $templateId = $projectTemplateModel->where(["project_id" => $projectId])->getField("id");
        return $templateId;
    }

    /**
     * 修改项目模板配置
     * @param $param
     * @return array
     */
    public function modifyTemplateConfig($param)
    {
        $projectTemplateModel = model(ProjectTemplateModel::class);
        $templateData = $projectTemplateModel->field("project_id,config")->where(["id" => $param["template_id"]])->find();
        $configData = json_decode($templateData["config"], true);

        if (!is_array($configData)) {
            $configData = [];
        }

        if (!array_key_exists($param["module_code"], $configData)) {
            $configData[$param["module_code"]] = [
                $param["category"] => $param["config"],
            ];
        } else {
            $configData[$param["module_code"]][$param["category"]] = $param["config"];
        }

        $resData = $projectTemplateModel->modifyItem([
            "id" => $param["template_id"],
            "config" => $configData,
        ]);

        if (!$resData) {
            // 修改项目模板配置失败错误码 - 004
            throw_strack_exception($projectTemplateModel->getError(), ErrorCode::PROJECT_TEMPLATE_MODIFY_FAILURE);
        } else {
            $message = string_initial_letter($param["module_code"] . '_' . $param["category"] . '_Template_SC', "_");
            if (in_array($param["category"], ["step", "step_fields"])) {
                $configFormat = json_decode($resData["config"], true);
                $stepList = array_key_exists("step", $configFormat[$param["module_code"]]) ? $configFormat[$param["module_code"]]["step"] : [];
                $stepFields = array_key_exists("step_fields", $configFormat[$param["module_code"]]) ? $configFormat[$param["module_code"]]["step_fields"] : [];
                if (!empty($stepList)) {
                    $stepParam = [
                        "category" => $param["category"],
                        "module_code" => $param["module_code"],
                        "step_list" => $stepList,
                        "step_fields" => $stepFields,
                        "project_id" => $templateData["project_id"],
                    ];
                    $this->changeStepFieldsLinkView($stepParam);
                }
            }

            // 返回成功数据
            return success_response(L($message), $resData);
        }
    }

    /**
     * 改变任务表自定义字段相应的改变实体视图
     * @param $param
     * @param $mode
     * @return array
     */
    protected function changeBaseFieldsLinkEntity($param, $mode)
    {
        $resData = [];

        // 获取模版ID
        $templateId = $this->getProjectTemplateID($param["project_id"]);
        // 获取模版配置
        $templateConfig = $this->getTemplateConfig([
            "template_id" => $templateId,
            "module_code" => $param["module_code"],
        ]);

        // 处理数据修改
        if (array_key_exists($param["category"], $templateConfig)) {
            $fieldConfig = [];
            switch ($mode) {
                case "delete":
                    foreach ($templateConfig[$param["category"]] as $fieldItem) {
                        if (($fieldItem["field_type"] !== "custom") ||
                            ($fieldItem["field_type"] === "custom" && $param["original_fields"] !== $fieldItem["fields"])) {
                            array_push($fieldConfig, $fieldItem);
                        }
                    }
                    break;
                case "update":
                    $originalFields = array_column($templateConfig[$param["category"]], null, "fields");
                    if (array_key_exists($param["original_fields"], $originalFields) && $originalFields[$param["original_fields"]]["field_type"] === "custom") {
                        $originalFields[$param["original_fields"]]["lang"] = $param["variable_name"];
                        $originalFields[$param["original_fields"]]["fields"] = $param["update_fields"];
                        $originalFields[$param["original_fields"]]["id"] = $param["update_fields"];
                        $originalFields[$param["original_fields"]]["value_show"] = $param["update_fields"];
                        $originalFields[$param["original_fields"]]["field_group_id"] = "{$originalFields[$param["original_fields"]]["module_code"] }_{$param["update_fields"]}";
                    }
                    $fieldConfig = array_column($originalFields, null);
                    break;
            }

            try {
                $resData = $this->modifyTemplateConfig([
                    "module_code" => $param["module_code"],
                    "category" => $param["category"],
                    "template_id" => $templateId,
                    "config" => $fieldConfig,
                ]);
            } catch (\Exception $e) {

            }
        }

        return $resData;
    }

    /**
     * 保存模版与视图字段之间的联动
     * @param $param
     * @return array
     */
    protected function changeStepFieldsLinkView($param)
    {
        $viewService = new ViewService();
        $schemaService = SchemaService::getInstance();

        // 模版设置工序
        $templateStep = $param["step_list"];
        $stepCodeMap = array_column($templateStep, null, "code");

        // 模版设置工序字段
        $templateStepFields = $param["step_fields"];

        // 增加视图显示字段格式
        $masterModuleCode = "base";
        foreach ($templateStepFields as &$stepFieldItem) {
            $stepFieldItem["view_field"] = $schemaService->getFieldColumnName($stepFieldItem, $masterModuleCode);
        }
        $stepFieldsMap = array_column($templateStepFields, null, "view_field");

        // 获取所有的工序显示字段
        $showStepFields = array_column($templateStepFields, "view_field");
        // 获取工序显示的第一个字段
        $firstStepFields = Arr::first($showStepFields);
        // 获取除第一个以外的字段
        $fieldList = [];
        foreach ($showStepFields as $key => $fieldItem) {
            if ($key > 0) {
                array_push($fieldList, $fieldItem);
            }
        }

        $page = "project_{$param["module_code"]}";

        $filter = ["page" => $page, "project_id" => $param["project_id"]];
        // 自定义视图
        $viewListData = $viewService->getViewListData([
            "filter" => ["filter" => $filter, "fields" => "id,config"],
            "module_code" => "view",
        ]);
        // 获取默认视图
        $viewDefaultData = $viewService->getViewListData([
            "filter" => ["filter" => $filter, "fields" => "id,page,config"],
            "module_code" => "view_default",
        ]);

        $viewParam = [
            "config" => [],
            "first_field" => $firstStepFields,
            "field_list" => $fieldList,
            "step_code_map" => $stepCodeMap,
            "step_fields_map" => $stepFieldsMap,
            "step_show_field_list" => $showStepFields,
        ];

        $resData = [];
        if ($viewListData["total"] > 0 || $viewDefaultData["total"] > 0) {
            $viewList = array_merge($viewListData["rows"], $viewDefaultData["rows"]);
            foreach ($viewList as $viewData) {
                $viewParam["config"] = $viewData["config"];
                $viewData["config"] = $this->generateViewConfigData($viewParam);
                // 默认视图
                if (array_key_exists("page", $viewData)) {
                    $resData = $viewService->modifyDefaultView($viewData);
                } else { // 自定义视图
                    $resData = $viewService->modifyView($viewData);
                }
            }
        }

        return $resData;
    }

    /**
     * 处理一层字段数据
     * @param $param
     * @param string $mode
     * @return mixed
     */
    public function dealViewFirstFields($param, $mode = "add")
    {
        $firstFnameMap = [];
        $firstStepData = [];
        switch ($mode) {
            case "add":
                foreach ($param["first_field_data"] as $firstFieldItem) {
                    if (array_key_exists("step", $firstFieldItem) && $firstFieldItem["step"] === "yes") {
                        if (array_key_exists($firstFieldItem["fname"], $param["step_code_map"])) {
                            if (!$firstFieldItem["fhcol"]) {
                                $firstFieldItem["colspan"] = count($param["step_show_field_list"]);
                            } else {
                                $firstFieldItem["colspan"] = 1;
                            }
                            $firstFieldItem["first_field"] = $param["first_field"];
                            $firstFieldItem["field_list"] = join(",", $param["field_list"]);

                            array_push($firstStepData, $firstFieldItem);
                        } else {
                            $firstFieldMapData = array_column($firstStepData, null, "fname");
                            foreach ($param["step_code_map"] as $stepItem) {
                                if (!array_key_exists($stepItem["code"], $firstFieldMapData)) {
                                    $stepData = [
                                        "bgc" => $stepItem["color"],
                                        "but" => $stepItem["code"],
                                        "step" => "yes",
                                        "class" => "{$stepItem["code"]}_h",
                                        "fhcol" => true,
                                        "fname" => $stepItem["code"],
                                        "title" => $stepItem["name"],
                                        "colspan" => 1,
                                        "field_list" => join(",", $param["field_list"]),
                                        "first_field" => $param["first_field"],
                                        "frozen_status" => false,
                                        "hidden_status" => "no",
                                    ];
                                    array_push($firstStepData, $stepData);
                                }
                            }
                        }
                    } else {
                        array_push($firstStepData, $firstFieldItem);
                    }
                }
                $firstFnameMap = array_column($firstStepData, null, "fname");
                break;
            case "delete":
                foreach ($param["first_field_data"] as &$firstFieldItem) {
                    if (array_key_exists("step", $firstFieldItem) && $firstFieldItem["step"] === "yes") {
                        $fieldList = explode(",", $firstFieldItem["field_list"]);
                        array_push($fieldList, $firstFieldItem["first_field"]);
                        if (in_array($param["delete_field"], $fieldList)) {
                            $retainFields = array_diff($fieldList, [$param["delete_field"]]);
                            $firstField = Arr::first($retainFields);

                            array_splice($retainFields, array_search($firstField, $retainFields), 1);
                            $firstFieldItem["first_field"] = $firstField;
                            $firstFieldItem["field_list"] = join(",", $retainFields);
                            array_push($firstStepData, $firstFieldItem);
                        }
                    }
                }
                break;
        }

        return ["first_fname_map" => $firstFnameMap, "first_field_list" => $firstStepData];
    }

    /**
     * 处理step字段数据
     * @param $viewSecondFields
     * @param $valueShowFields
     * @param $field
     * @param $param
     * @param $stepItem
     * @return mixed
     */
    protected function dealStepFieldData(&$viewSecondFields, $valueShowFields, $field, $param, $stepItem)
    {
        // 工序最后一个字段
        $lastStepFields = end($param["step_show_field_list"]);
        if (in_array($field, [$param["first_field"], $lastStepFields])) {
            $viewSecondFields[$valueShowFields]["bdc"] = $stepItem["color"];
            $viewSecondFields[$valueShowFields]["cellClass"] = "datagrid-cell-c1-{$valueShowFields}";
            $viewSecondFields[$valueShowFields]["deltaWidth"] = 1;
            if ($field == $param["first_field"]) {
                $viewSecondFields[$valueShowFields]["hidden"] = false;
                $viewSecondFields[$valueShowFields]["cbd"] = "colleft";
                $viewSecondFields[$valueShowFields]["step_index"] = "first";
            } else {
                $hidden = $param["first_fname_map"][$stepItem["code"]]["colspan"] > 1 ? false : true;
                $viewSecondFields[$valueShowFields]["hidden"] = $hidden;
                $viewSecondFields[$valueShowFields]["cbd"] = "colright";
                $viewSecondFields[$valueShowFields]["step_index"] = "last";
            }
        } else {
            $hidden = $param["first_fname_map"][$stepItem["code"]]["colspan"] > 1 ? false : true;
            $viewSecondFields[$valueShowFields]["hidden"] = $hidden;
            $viewSecondFields[$valueShowFields]["step_index"] = "";
            $viewSecondFields[$valueShowFields] = array_diff_key($viewSecondFields[$valueShowFields], ["cellClass" => "", "bdc" => "", "cbd" => "", "deltaWidth" => ""]);
        }
        return $viewSecondFields;
    }

    /**
     * 处理视图字段数据
     * @param $param
     * @return mixed
     */
    private function generateViewConfigData($param)
    {
        $viewData = $param["config"];

        // 处理双层表头中 第一层数据
        $countLayer = count($viewData["fields"]);
        if ($countLayer === 2) {
            $param["first_field_data"] = $viewData["fields"][0];
            $firstFieldData = $this->dealViewFirstFields($param, "add");
            $firstFnameMap = $firstFieldData["first_fname_map"];
            $viewData["fields"][0] = $firstFieldData["first_field_list"];

            // 处理双层表头中 第二层数据
            $viewSecondFields = [];
            $viewSecondFieldMap = array_column($viewData["fields"][1], null, "field");
            foreach ($viewSecondFieldMap as $key => $secondFieldItem) {
                if (array_key_exists("step", $secondFieldItem) && $secondFieldItem["step"]) {
                    if (array_key_exists($secondFieldItem["belong"], $firstFnameMap)) {

                        foreach ($param["step_show_field_list"] as $stepFieldItem) {
                            $valueShowFields = $secondFieldItem["belong"] . "_" . $stepFieldItem;
                            if (!array_key_exists($valueShowFields, $viewSecondFields)) {
                                if (array_key_exists($valueShowFields, $viewSecondFieldMap)) {
                                    $viewSecondFields[$valueShowFields] = $viewSecondFieldMap[$valueShowFields];
                                } else {
                                    $addStepFields = [
                                        "drag" => false,
                                        "step" => true,
                                        "align" => "center",
                                        "field" => "{$secondFieldItem["belong"]}_{$param["step_fields_map"][$stepFieldItem]["view_field"]}",
                                        "title" => $param["step_fields_map"][$stepFieldItem]["lang"],
                                        "width" => 120,
                                        "belong" => $secondFieldItem["belong"],
                                        "findex" => 0,
                                        "hidden" => false,
                                        "step_index" => "",
                                        "frozen_status" => false,
                                    ];
                                    $viewSecondFields[$valueShowFields] = $addStepFields;
                                }
                            }
                            $param["first_fname_map"] = $firstFnameMap;
                            $this->dealStepFieldData($viewSecondFields, $valueShowFields, $stepFieldItem, $param, $param["step_code_map"][$secondFieldItem["belong"]]);
                        }
                    } else {
                        foreach ($param["step_code_map"] as &$stepItem) {
                            $stepItem["step_show_field_list"] = $param["step_show_field_list"];
                        }

                        foreach ($param["step_code_map"] as $stepKey => $stepCodeItem) {
                            foreach ($stepCodeItem["step_show_field_list"] as $fieldItem) {
                                $valueShowFields = $stepKey . "_" . $fieldItem;

                                $addStepFields = [
                                    "drag" => false,
                                    "step" => true,
                                    "align" => "center",
                                    "field" => "{$stepKey}_{$param["step_fields_map"][$fieldItem]["view_field"]}",
                                    "title" => $param["step_fields_map"][$fieldItem]["lang"],
                                    "width" => 120,
                                    "belong" => $stepKey,
                                    "findex" => 0,
                                    "hidden" => false,
                                    "step_index" => "",
                                    "frozen_status" => false,
                                ];
                                $viewSecondFields[$valueShowFields] = $addStepFields;
                                $param["first_fname_map"] = $firstFnameMap;
                                $this->dealStepFieldData($viewSecondFields, $valueShowFields, $fieldItem, $param, $stepCodeItem);
                            }
                        }
                    }
                } else {
                    $viewSecondFields[$secondFieldItem["field"]] = $secondFieldItem;
                }
            }

            $sortSecondsViewFields = array_column($viewSecondFields, null);
            foreach ($sortSecondsViewFields as $key => &$item) {
                if (array_key_exists("findex", $item)) {
                    $item["findex"] = $key;
                }
            }
            $viewData["fields"][1] = $sortSecondsViewFields;
        }

        return $viewData;
    }

    /**
     * 获取数据表格视图模板配置数据（分组、排序、工序、工序字段）
     * @param $param
     * @return array
     */
    public function getGridViewTemplateConfig($param)
    {
        // 获取当前模块项目模版配置
        $moduleTemplateConfig = $this->getTemplateConfig($param);

        // 定义变量
        $groupConfig = [];
        $sortConfig = ["sort_data" => [], "sort_query" => []];
        $stepListConfig = [];
        $stepFieldsConfig = [];

        //  存在项目模板配置
        if (!empty($moduleTemplateConfig)) {

            // 工序配置列表和工序字段缺一不可
            if (array_key_exists("step", $moduleTemplateConfig) && !empty($moduleTemplateConfig["step"]) &&
                array_key_exists("step_fields", $moduleTemplateConfig) && !empty($moduleTemplateConfig["step_fields"])
            ) {
                $stepListConfig = $moduleTemplateConfig['step'];
                $stepFieldsConfig = $moduleTemplateConfig['step_fields'];
            }
        }
        return [
            "group" => $groupConfig,
            "sort" => $sortConfig,
            "step_list" => $stepListConfig,
            "step_fields" => $stepFieldsConfig,
        ];
    }

    /**
     * 获取项目模板指定模块目标配置
     * @param $templateId
     * @param $moduleCode
     * @param $keyName
     * @return array|mixed
     * @throws \Exception
     */
    public function getConfigTargetSetting($templateId, $moduleCode, $keyName)
    {
        $projectTemplateModel = model(ProjectTemplateModel::class);
        $configData = $projectTemplateModel->findData([
            "fields" => "config",
            "filter" => ["id" => $templateId],
        ]);

        if (array_key_exists($moduleCode, $configData["config"])) {
            if (array_key_exists($keyName, $configData["config"][$moduleCode])) {
                return $configData["config"][$moduleCode][$keyName];
            } else {
                return [];
            }
        } else {
            return [];
        }
    }

    /**
     * 获得配置
     * @param $filter
     * @param $key
     * @return mixed
     */
    public function getConfigSetting($filter, $key)
    {
        $res = $this->projectTemplateModel
            ->where([$filter])
            ->getField("JSON_UNQUOTE(JSON_EXTRACT(config,'$.\"{$key}\"')) as setting", false);

        if (empty($res)) {
            throw new LogicException("can`t found project template", ErrorCode::PROJECT_TEMPLATE_NOT_FOUND);
        }

        return json_decode($res, true);
    }

    /**
     * 查找项目模板计划
     * @param $param
     * @return array|bool|mixed
     */
    public function findPlanTemplate($param)
    {
        try {
            $planData = $this->getConfigSetting($param['filter'], 'step_category_plan');
        } catch (\Throwable $e) {
            if ($e->getCode() != ErrorCode::PROJECT_TEMPLATE_NOT_FOUND) {
                throw $e;
            }
        }

        $planProjectData = [
            'start_time' => time(),
            'end_time' => time() + 3600 * 24 * 30 * 6,
        ];
        $categoryIdData = [];
        $needInit = empty($planData);
        if (!empty($planData)) {
            $planCategoryData = array_column($planData['tasks'], null, 'step_category_id');
            $planProjectData = $planData['project'];
            $categoryIdData = array_column($planCategoryData, 'step_category_id', 'step_category_id');
        }

        $stepCategoryQuery = $this->stepCategoryModel
            ->order("index asc");

        //当有保存的计划 过滤分类数据
        if (!empty($categoryIdData)) {
            $stepCategoryQuery->where(['id' => ['IN', implode(',', $categoryIdData)]]);
        }
        $categoryData = $stepCategoryQuery->select();
        //初始化
        $needInit && $this->initStepCategoryPlanTemplate($param['filter'], $categoryData, $planProjectData);
        $categoryData = array_column($categoryData, null, 'id');

        $tree = new Tree('id', 'parent_id');
        $tree->load($categoryData);
        $categoryData = $tree->DeepTree();

        $tree->traverseTree($categoryData, function (&$item) use (&$planCategoryData) {
            if (isset($planCategoryData[$item['id']])) {
                $item['start_time'] = $planCategoryData[$item['id']]['start_time'];
                $item['end_time'] = $planCategoryData[$item['id']]['end_time'];
            } else {
                $item['start_time'] = time();
                $item['end_time'] = time() + 3600 * 24 * 30;
            }
            StepService::formatGanttItem($item);
        });

        return StepService::formatStepCategoryPlanData($categoryData, $planProjectData);
    }

    /**
     * 更新模板计划
     * @param $data
     * @return array
     */
    public function updatePlanTemplate($data)
    {
        $config = $this->projectTemplateModel->where(['id' => $data['id']])
            ->field("id,config")
            ->findData();
        if (empty($config)) {
            throw_strack_exception("can`t found project template", ErrorCode::PROJECT_TEMPLATE_NOT_FOUND);
        }

        $updateData = [
            'id' => $data['id'],
            'config' => $config['config'],
        ];
        $deleted = $data['step_category_plan']['deleted'];
        $updated = $data['step_category_plan']['updated'];
        $updated = array_column($updated, null, 'step_category_id');
        $updatedKeys = array_keys($updated);

        $projectData = &$data['step_category_plan']['project'];
        $projectData['start_time'] = strtotime($projectData['start_time']);
        $projectData['end_time'] = strtotime($projectData['end_time']);
        $projectData['duration'] = $projectData['end_time'] - $projectData['start_time'];

        //更新 project data
        $updateData['config']['step_category_plan']['project'] = array_merge($updateData['config']['step_category_plan']['project'], $projectData);

        $planData = $updateData['config']['step_category_plan']['tasks'];

        $stepCategoryModel = model(StepCategoryModel::class);


        $stepCategoryData = $stepCategoryModel->order("index ASC")->select();
        $stepCategoryDataAssoc = array_column($stepCategoryData, null, 'id');
        $tree = new Tree('id', 'parent_id');

        $tree->load($stepCategoryData);
        $stepCategoryTree = $tree->DeepTree();

        $deepChild = [];
        $tree->traverseTree($stepCategoryTree, function ($item) use (&$deepChild) {
            if (!isset($item['children']) || empty($item['children'])) {
                $deepChild[] = $item['id'];
            }
        });

        foreach ($planData as $key => &$item) {
            if (in_array($item['step_category_id'], $deleted)) {
                unset($planData[$key]);
                continue;
            }

            //标记是否有子分类
            if (in_array($item['step_category_id'], $deepChild)) {
                $item['has_child'] = 0;
            } else {
                $item['has_child'] = 1;
            }

            if (in_array($item['step_category_id'], $updatedKeys)) {
                $item['start_time'] = strtotime($updated[$item['step_category_id']]['start_time']);
                $item['end_time'] = strtotime($updated[$item['step_category_id']]['end_time']);
                $item['duration'] = $item['end_time'] - $item['start_time'];
            }

            $item['start_time_text'] = date("Y-m-d H:i:s", $item['start_time']);
            $item['end_time_text'] = date("Y-m-d H:i:s", $item['end_time']);
            $item['step_category_index'] = $stepCategoryDataAssoc[$item['step_category_id']]['index'];

            //一个工序 最长sku 时长
            $item['unit_longest_duration'] = $item['duration'];
        }
        $planData = array_values($planData ?? []);
        $planData = array_sort_by($planData, 'step_category_index');
        $updateData['config']['step_category_plan']['tasks'] = $planData;
        $flag = $this->projectTemplateModel->modifyItem($updateData);

        if ($flag === false) {
            throw_strack_exception($this->projectTemplateModel->getError(), $this->projectTemplateModel->getErrorCode());
        }
        return $flag;
    }

    /**
     * 初始化工序排期计划模板
     * @param $filter
     * @param $stepCategoryData
     * @param $projectData
     * @return array|bool|mixed
     */
    public function initStepCategoryPlanTemplate($filter, $stepCategoryData, $projectData)
    {
        $projectData['duration'] = $projectData['end_time'] - $projectData['start_time'];

        $lastStartTime = $projectData['start_time'];

        $taskData = array_map(function ($item) use (&$projectData, &$lastStartTime) {
            $item['step_category_id'] = $item['id'];
            $item['step_category_name'] = $item['name'];
            $item['step_category_code'] = $item['code'];

            $item['start_time'] = time();
            $item['end_time'] = time() + 3600 * 24 * 3;
            $item['duration'] = $item['end_time'] - $item['start_time'];
            $item['duration_percent'] = number_floor_precision($item['duration'] / $projectData['duration'], 2);
            $item['offset_percent'] = number_floor_precision(($item['start_time'] - $projectData['start_time']) / $projectData['duration'], 2);
            $item['latest_offset'] = number_floor_precision($item['start_time'] - $lastStartTime, 2);
            $item['unit_longest_duration'] = $item['duration'];
            $item['step_category_index'] = $item['index'];

            $lastStartTime = $item['start_time'];
            return $item;
        }, $stepCategoryData);

        $this->projectTemplateModel->resetDefault();
        $data = $this->projectTemplateModel->where($filter)->findData([
            'field' => "id,config",
        ]);

        //移除获取器自动生成的属性
        unset($data['json']);

        $data['config']['step_category_plan']['project'] = $projectData;
        $data['config']['step_category_plan']['tasks'] = $taskData;
        return $this->projectTemplateModel->modifyItem($data);
    }

    /**
     * 格式化项目模板统计工序模块化数据
     * @param $data
     * @return mixed
     */
    protected function formatStatisticalStepData(&$data)
    {
        $stepCategoryIds = [];
        $stepIds = [];

        foreach ($data as $item) {
            if ($item['type'] === 'step_category') {
                $stepCategoryIds[] = $item['id'];
            } else if ($item['type'] === 'step') {
                $stepIds[] = $item['id'];
            }
        }

        $stepModel = model(StepModel::class);
        $stepCategoryModel = model(StepCategoryModel::class);
        $stepCategoryDict = [];
        $stepDict = [];
        // 工序分类字典
        $stepCategoryDictData = $stepCategoryModel->field('id,name,code')->where(['id' => ['IN', join(',', $stepCategoryIds)]])->select();
        if (!empty($stepCategoryDictData)) {
            $stepCategoryDict = array_column($stepCategoryDictData, null, 'id');
        }
        // 工序字典
        $stepDictData = $stepModel->field('id,name,code')->where(['id' => ['IN', join(',', $stepIds)]])->select();
        if (!empty($stepDictData)) {
            $stepDict = array_column($stepDictData, null, 'id');
        }

        foreach ($data as &$item) {
            if ($item['type'] === 'step_category') {
                if (array_key_exists($item['id'], $stepCategoryDict)) {
                    $item['name'] = $stepCategoryDict[$item['id']]['name'];
                    $item['code'] = $stepCategoryDict[$item['id']]['code'];
                } else {
                    $item['name'] = '';
                    $item['code'] = '';
                }
            } else if ($item['type'] === 'step') {
                if (array_key_exists($item['id'], $stepDict)) {
                    $item['name'] = $stepDict[$item['id']]['name'];
                    $item['code'] = $stepDict[$item['id']]['code'];
                } else {
                    $item['name'] = '';
                    $item['code'] = '';
                }
            }

            $item['id'] = (int)$item['id'];
        }


        return $data;
    }


    /**
     * 任务统计工序项目模板查询
     * @param $param
     * @return array
     */
    public function findStatisticalStepTemplate($param = [])
    {
        try {
            $statisticalStepData = OptionsService::getInstance()->getProducerStepCategoryConfig();
        } catch (LogicException $e) {
            if ($e->getCode() !== ErrorCode::PRODUCER_STEP_CATEGORY_NOT_CONFIG) {
                //获取默认配置
                throw $e;
            }
            $statisticalStepData = [];
        }
        $configMap = [];
        foreach ($statisticalStepData as $statisticalStepDatum) {
            $configMap[$statisticalStepDatum['id']] = $statisticalStepDatum;
        }
        $extFilter = $param['ext_filter'] ?? '';
        $totalSecondLevelStepCategoryList = StepCategoryService::getInstance()->secondLevelList($extFilter);
        foreach ($totalSecondLevelStepCategoryList as &$item) {
            if (empty($configMap[$item['id']])) {
                $item['is_show'] = 'yes';
                $item['is_enable_census'] = 'no';
                $item['show_name'] = $item['name'];
                $item['show_index'] = intval($item['index']);
//                $item['color'] = $item['color'];
                $item['upstream_step_category_id'] = [];
                $item['delay_order_task_review_workflow_id'] = 0;
                continue;
            }
            $item['is_show'] = $configMap[$item['id']]['is_show'] ?? 'yes';
            $item['is_enable_census'] = $configMap[$item['id']]['is_enable_census'] ?? 'no';
            $item['show_name'] = $configMap[$item['id']]['show_name'] ?? $item['name'];
            $item['show_index'] = $configMap[$item['id']]['show_index'] ?? $item['index'];
            $item['show_index'] = intval($item['show_index']);
            $item['color'] = $configMap[$item['id']]['color'] ?? $item['color'];
            $item['upstream_step_category_id'] = $configMap[$item['id']]['upstream_step_category_id'] ?? [];
            $item['upstream_step_category_id'] = array_map('intval', $item['upstream_step_category_id']);
            $item['delay_order_task_review_workflow_id'] = $configMap[$item['id']]['delay_order_task_review_workflow_id'] ?? 0;
            $item['delay_order_task_review_workflow_id'] = intval($item['delay_order_task_review_workflow_id']);
        }
        return array_sort_by($totalSecondLevelStepCategoryList, 'show_index', 'asc', false);
    }

    /**
     * 更新任务统计工序项目模板配置
     * @param $data
     * @return array
     * @throws \Exception
     */
    public function updateStatisticalStepTemplate($data)
    {
        OptionsService::getInstance()->getProducerStepCategoryConfig();
        OptionsService::getInstance()->updateOptionsData([
            'options_name' => 'producer_step_category_config',
            'config' => $data['producer_step_category_config'],
        ]);

        return OptionsService::getInstance()->getProducerStepCategoryConfig();
    }

    /**
     * 获得默认模块字段配置
     * @return array
     */
    private function getDefaultModuleFieldConfig()
    {
        $stepCategoryList = model(StepCategoryModel::class)->where(['entity_id' => ['GT', 0], 'parent_id' => 0])->select();
        if (count($stepCategoryList) === 0) {
            return [];
        }
        $stepCategoryModuleNameMap = array_column($stepCategoryList, 'name', 'entity_id');
        $modules = model(ModuleModel::class)->where(['id' => ['IN', implode(',', array_column($stepCategoryList, 'entity_id'))]])->select();
        $res = [];
        foreach ($modules as $module) {
            $fixedFields = array_keys(Module::$moduleDictData['field_index_by_code'][$module['code']]['fixed'] ?? []);
            $customFields = array_keys(Module::$moduleDictData['field_index_by_code'][$module['code']]['custom'] ?? []);

            $fields = array_map(function ($field) use ($module) {
                return [
                    'id' => "{$module['id']}-$field",
                    'name' => $field,
                    'code' => $field,
                    'default_prefix' => '',
                    'number_pad_length' => 0,
                    'number_pad_length_min' => 0,
                    'number_pad_length_max' => 0,
                    'children' => [],
                ];
            }, array_merge($fixedFields, $customFields));

            $res[] = [
                'id' => $module['id'],
                'name' => $stepCategoryModuleNameMap[$module['id']],
                'code' => $module['code'],
                'default_prefix' => '',
                'number_pad_length' => 0,
                'number_pad_length_min' => 0,
                'number_pad_length_max' => 0,
                'children' => $fields,
            ];
        }
        return $res;
    }

    /**
     * 查询模块字段配置
     * @param $filter
     * @return array
     * @throws \Exception
     */
    public function findModuleFieldConfig($filter)
    {
        $template = $this->projectTemplateModel->where($filter)->find();
        if (!empty($template)) {
            $config = json_decode($template['module_field_config'], true);
            if (empty($config)) {
                $config = $this->getDefaultModuleFieldConfig();
            } else {
                // 字段变动 合并
                $defaultConfigList = $this->getDefaultModuleFieldConfig();
                $config = array_column($config, null, 'code');
                foreach ($defaultConfigList as &$defaultConfig) {
                    if (isset($config[$defaultConfig['code']]['children'])) {
                        $defaultConfig['children'] = array_values(array_column($config[$defaultConfig['code']]['children'], null, 'code') + array_column($defaultConfig['children'], null, 'code'));
                    }
                }
                $config = $defaultConfigList;
            }
            $this->projectTemplateModel
                ->modifyItem([
                    'id' => $template['id'],
                    'module_field_config' => $config,
                ]);
            $res = $config;
        } else {
            $res = $this->getDefaultModuleFieldConfig();
        }
        return $res;
    }

    /**
     * 更新模块字段配置
     * @param $data
     * @return array|bool
     * @throws \Exception
     */
    public function updateModuleFieldConfig($data)
    {
        $template = $this->projectTemplateModel
            ->where(['id' => $data['id']])
            ->find();
        if (empty($template)) {
            throw_strack_exception('missing project template', ErrorCode::PROJECT_TEMPLATE_NOT_FOUND);
        }
        return $this->projectTemplateModel->modifyItem(['id' => $template['id'], 'module_field_config' => $data['module_field_config']]);
    }

    /**
     * 查询项目模板的模块字段配置
     * @param int $id
     * @return array
     */
    public function getProjectTemplateModuleFieldConfig(int $id)
    {
        $template = model(ProjectTemplateModel::class)->where(['id' => $id])->find();
        if (empty($template)) {
            return [];
        }
        if (empty($template['module_field_config'])) {
            return [];
        }
        $moduleConfig = json_decode($template['module_field_config'], true);

        $moduleConfig = array_column($moduleConfig, null, 'code');
        foreach ($moduleConfig as &$configItem) {
            $configItem['fields'] = array_column($configItem['children'], null, 'code');
        }
        return $moduleConfig;
    }

    /**
     * 检测项目模板中目录模板是否为空，为空则不能选择此项目模板
     * @param $templateId
     * @return mixed|void
     */
    public function checkProjectTemplate($templateId)
    {
        $projectTemplate = model(ProjectTemplateModel::class)->field('directory_template_id')->find($templateId);
        if (!empty($projectTemplate) && empty($projectTemplate['directory_template_id'])) {
            throw new LogicException("the project template no choose", ErrorCode::THE_PROJECT_TEMPLATE_NO_CHOOSE);
        }

        if (!empty($projectTemplate['directory_template_id'])) {
            $directoryTemplate = model(DirectoryTemplateModel::class)->field('template_content')->find($projectTemplate['directory_template_id']);
            return $directoryTemplate['template_content'];
        }
    }

    /**
     * 任务统计工序项目模板查询
     * @return array
     * @throws \think\Exception
     */
    public function findStepCategoryShowList()
    {
        $projectStepCategoryConfigList = TemplateService::getInstance()->findStatisticalStepTemplate();

        $showStepCategoryList = array_filter($projectStepCategoryConfigList, function ($item) {
            return $item['is_show'] === 'yes';
        });
        if (empty($showStepCategoryList)) {
            return [];
        }

        $parentIdList = array_filter(array_unique(array_column($showStepCategoryList, 'parent_id')));
        $entityIdMapList = model(StepCategoryModel::class)->where(['id' => ['IN', implode(',', $parentIdList)]])->field('id,entity_id')->select();
        $entityIdMapList = array_column($entityIdMapList, 'entity_id', 'id');

        foreach ($showStepCategoryList as &$showStepCategoryItem) {
            if (!empty($showStepCategoryItem['parent_id'])) {
                $showStepCategoryItem['module_code'] = module_by_id($entityIdMapList[$showStepCategoryItem['parent_id']] ?? 0, false)['code'] ?? '';
            }
        }

        return array_values($showStepCategoryList);
    }

    /**
     * 任务统计工序项目模板查询
     * @param $projectTemplateIdList
     * @return array
     * @throws Exception
     */
    public function getStatisticalStepTemplateList($projectTemplateIdList)
    {
        $projectTemplateSettingList = $this->projectTemplateModel
            ->where(['id' => ['IN', $projectTemplateIdList]])
            ->field("id,JSON_UNQUOTE(JSON_EXTRACT(config,'$.producer_step_category_config')) as setting")
            ->select();

        $totalSecondLevelStepCategoryList = StepCategoryService::getInstance()->secondLevelList();
        foreach ($projectTemplateSettingList as &$projectTemplateSetting) {
            $statisticalStepData = json_decode($projectTemplateSetting['setting'], true);
            $statisticalStepData = is_array($statisticalStepData) ? $statisticalStepData : [];
            $configMap = [];
            foreach ($statisticalStepData as $statisticalStepDatum) {
                $configMap[$statisticalStepDatum['id']] = $statisticalStepDatum;
            }
            $projectTemplateSetting['config'] = $totalSecondLevelStepCategoryList;

            foreach ($projectTemplateSetting['config'] as &$item) {
                if (empty($configMap[$item['id']])) {
                    $item['is_show'] = 'yes';
                    $item['is_enable_census'] = 'no';
                    $item['show_name'] = $item['name'];
                    $item['show_index'] = intval($item['index']);
                    continue;
                }
                $item['is_show'] = $configMap[$item['id']]['is_show'] ?? 'yes';
                $item['is_enable_census'] = $configMap[$item['id']]['is_enable_census'] ?? 'no';
                $item['show_name'] = $configMap[$item['id']]['show_name'] ?? $item['name'];
                $item['show_index'] = $configMap[$item['id']]['show_index'] ?? $item['index'];
                $item['show_index'] = intval($item['show_index']);
                $item['color'] = $configMap[$item['id']]['color'] ?? $item['color'];
            }
            $projectTemplateSetting['config'] = array_sort_by($projectTemplateSetting['config'], 'show_index', 'asc', false);
        }
        return $projectTemplateSettingList;
    }

    /**
     * 项目模板环节配置查询
     * @param $param
     * @return array
     */
    public function findStepCategoryConfig($param = [])
    {
        try {
            $statisticalStepData = $this->getConfigSetting($param['filter'], 'producer_step_category_config');
        } catch (LogicException $e) {
            if ($e->getCode() !== ErrorCode::PROJECT_TEMPLATE_NOT_FOUND) {
                //获取默认配置
                throw $e;
            }
            $statisticalStepData = [];
        }
        $configMap = [];
        foreach ($statisticalStepData as $statisticalStepDatum) {
            $configMap[$statisticalStepDatum['id']] = $statisticalStepDatum;
        }
        $totalSecondLevelStepCategoryList = $this->findStatisticalStepTemplate();
        foreach ($totalSecondLevelStepCategoryList as &$item) {
            if (empty($configMap[$item['id']])) {
                continue;
            }
            $item['is_show'] = $configMap[$item['id']]['is_show'] ?? 'yes';
            $item['is_enable_census'] = $configMap[$item['id']]['is_enable_census'] ?? 'no';
            $item['show_name'] = $configMap[$item['id']]['show_name'] ?? $item['name'];
            $item['show_index'] = $configMap[$item['id']]['show_index'] ?? $item['index'];
            $item['show_index'] = intval($item['show_index']);
            $item['color'] = $configMap[$item['id']]['color'] ?? $item['color'];
            $item['upstream_step_category_id'] = $configMap[$item['id']]['upstream_step_category_id'] ?? [];
            $item['upstream_step_category_id'] = array_map('intval', $item['upstream_step_category_id']);
            $item['delay_order_task_review_workflow_id'] = $configMap[$item['id']]['delay_order_task_review_workflow_id'] ?? 0;
            $item['delay_order_task_review_workflow_id'] = intval($item['delay_order_task_review_workflow_id']);
        }
        return array_sort_by($totalSecondLevelStepCategoryList, 'show_index', 'asc', false);
    }

    /**
     * 更新任务统计工序项目模板配置
     * @param $data
     * @return array
     * @throws \Exception
     */
    public function updateStepCategoryConfig($data)
    {
        $param = [
            'filter' => [
                'id' => $data['id'],
            ],
            'fields' => 'id,config',
        ];
        $config = $this->projectTemplateModel->findData($param);

        if (empty($config)) {
            throw new LogicException("can`t found project template", ErrorCode::PROJECT_TEMPLATE_NOT_FOUND);
        }
        $config['config']['producer_step_category_config'] = $data['producer_step_category_config'];

        $flag = $this->projectTemplateModel->modifyItem($config);

        if ($flag === false) {
            throw new LogicException($this->projectTemplateModel->getError(), $this->projectTemplateModel->getErrorCode());
        }

        return $this->findStepCategoryConfig(['filter' => ['id' => $data['id']]]);
    }
}
